package com.ouc.common.ncRead.impl;

import com.ouc.common.constant.NcConstants;
import com.ouc.common.constant.VariableName;
import com.ouc.common.entity.*;
import com.ouc.common.ncRead.NcDao;
import com.ouc.common.utils.*;
import com.ouc.common.utils.file.FileUtils;
import com.ouc.common.utils.nc.NcUtil;
import org.springframework.lang.Nullable;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.AsyncResult;
import org.springframework.stereotype.Repository;
import ucar.ma2.InvalidRangeException;
import ucar.nc2.Dimension;
import ucar.nc2.NetcdfFile;
import ucar.nc2.Variable;

import java.io.IOException;
import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.text.ParseException;
import java.util.*;
import java.util.concurrent.Future;

import static java.lang.Float.*;


@Repository
//@CacheConfig(cacheNames = {"ncData_cache"}, keyGenerator = "myKeyGenerator")
public class NcDaoImpl implements NcDao {
  private Integer scale = 1000;//保留小数位数

  @Override
  public Variable getVariable(NetcdfFile file, String variableName) throws IOException {
    if (file.findVariable(variableName) != null) {
      return file.findVariable(variableName);
    }
    VariableName[] variableNames = VariableName.values();   //获取所有变量列表
    for (VariableName name : variableNames) {
      String[] strings = name.getVariableName();  //获取每个变量的所有对应字符串
      for (String string : strings) {
        //对于每个字符串，判断其是否与variableName相匹配。如果匹配，在该变量下的字符串数组中，获取变量
        if (variableName.equalsIgnoreCase(string)) {
          return getVariable(file, name);
        }
      }
    }
    return null;
  }


  @Override
  public Variable getVariable(NetcdfFile file, VariableName variableName) {
    String[] strings = variableName.getVariableName();
    for (String string : strings) {
      if (file.findVariable(string) != null) {
        return file.findVariable(string);
      }
    }
    return null;
  }


  @Override
//  @Cacheable
  public NcGridData getNcGridData(NetcdfFile file, String variableName) throws IOException, InvalidRangeException {
    return getNcGridData(file, variableName, null);
  }

  @Override
//  @Cacheable
  public NcGridData getNcGridData(NetcdfFile file, String variableName, Integer depth) throws IOException, InvalidRangeException {
    return getNcGridData(file, variableName, null, null, depth);
  }

  @Override
//  @Cacheable
  public NcGridData getNcGridData(NetcdfFile file, String variableName, Float[] lon, Float[] lat) throws IOException, InvalidRangeException {
    return getNcGridData(file, variableName, lon, lat, null);
  }

  @Override
//  @Cacheable
  public NcGridData getNcGridData(NetcdfFile file, String variableName, Float[] lon, Float[] lat, @Nullable Integer depth) throws IOException, InvalidRangeException {

    return getNcGridData(file, variableName, lon, lat, depth, null);
  }

  @Override
//  @Cacheable
  public NcGridData getNcGridData(NetcdfFile file, String variableName, Float[] lon, Float[] lat, Integer positionOf0, Integer positionOf1) throws IOException, InvalidRangeException {
    Variable variable = getVariable(file, variableName);
    NcGridData ncGridData = new NcGridData();
    if (variable == null) {
      return ncGridData;
    }
    ncGridData.setVariableName(variableName);
    //设置深度
    Variable variableLevel = getVariable(file, VariableName.LEVEL);
    if (variableLevel == null) {
      ncGridData.setDepth(null);
      ncGridData.setCurrLevel(0);
      ncGridData.setCurrDepth(0);
    } else {
      float[] listLevel = NcUtil.getDataOfSlice(variableLevel).getData1D();
      ncGridData.setDepth(listLevel);
      positionOf0 = MathUtils.middle(0, positionOf0 == null ? 0 : positionOf0, listLevel.length - 1);
      ncGridData.setCurrLevel(positionOf0);
      ncGridData.setCurrDepth(listLevel[positionOf0]);
    }
    if (positionOf1 != null) {
      Variable variableTime = getVariable(file, VariableName.TIME);
      ncGridData.setTimeArr(NcUtil.getDataOfSlice(variableTime).getData1D());
      ncGridData.setTime(positionOf1);
    }
    //设置经纬度
    Integer[] range0 = null;
    Integer[] range1 = null;
    Integer[] range0AndBound = null;
    int startIndex;
    int section;
    int endIndex;

    Variable variableLon = getVariable(file, VariableName.LON);
    boolean flag = false;
    NcData ncDataLon = NcUtil.getDataOfSlice(variableLon);
    //  如果经度限制是空的话设置进去的是一整层的lon数据
    if (StringUtils.isNotEmpty(lon)) {
      if (lon[1] == null || lon[0] == null || lon[1] > lon[0]) {
        startIndex = lon[0] == null ? 0 : ArrayUtils.getArrayIndex(ncDataLon.getData1D(), lon[0]);
        endIndex = lon[1] == null ? ncDataLon.getData1D().length - 1 : ArrayUtils.getArrayIndex(ncDataLon.getData1D(), lon[1]);
        section = Math.abs(endIndex - startIndex) + 1;
        range0 = new Integer[]{startIndex, section};
        ncDataLon = NcUtil.getDataWithLimit(variableLon, range0);
      }
      /**
       * 适用于开始经度大于结束经度
       * 一般是考虑地球球体原因导致
       */
      else if (lon[1] < lon[0]) {
        startIndex = ArrayUtils.getArrayIndex(ncDataLon.getData1D(), lon[0]);
        endIndex = ncDataLon.getData1D().length - 1;
        section = endIndex - startIndex + 1;
        range0 = new Integer[]{startIndex, section};
        float[] lonData1 = NcUtil.getDataWithLimit(variableLon, range0).getData1D();

        startIndex = 0;
        endIndex = ArrayUtils.getArrayIndex(ncDataLon.getData1D(), lon[1]);
        section = endIndex - startIndex + 1;
        range0AndBound = new Integer[]{startIndex, section};
        float[] lonData2 = NcUtil.getDataWithLimit(variableLon, range0AndBound).getData1D();
        float[] listLon = ArrayUtils.arrContact(lonData1, lonData2);
        ncDataLon.setData1D(listLon);
        flag = true;
      }
      /**
       * 一般不会走到此方法，因为这种情况适用于开始经度==结束经度
       * 是一条条带，但是一般可以使用设置经度范围为null来解决此种情况
       * 这里只是为了代码的严谨性（所有情况都要考虑）
       */
      else if (lon[1].equals(lon[0])) {
        startIndex = 0;
        endIndex = ncDataLon.getData1D().length - 1;
        section = Math.abs(endIndex - startIndex) + 1;
        range0 = new Integer[]{startIndex, section};
        ncDataLon = NcUtil.getDataOfSlice(getVariable(file, VariableName.LON));
      }
      ncGridData.setListLon(ncDataLon.getData1D());
    }
    ncGridData.setListLon(ncDataLon.getData1D());
    Variable variableLat = getVariable(file, VariableName.LAT);
    NcData ncDataLat = NcUtil.getDataOfSlice(variableLat);
    if (StringUtils.isNotEmpty(lat)) {
      startIndex = lat[0] == null ? 0 : ArrayUtils.getArrayIndex(ncDataLat.getData1D(), lat[0]);
      endIndex = lat[1] == null ? ncDataLat.getData1D().length - 1 : ArrayUtils.getArrayIndex(ncDataLat.getData1D(), lat[1]);
      section = Math.abs(endIndex - startIndex) + 1;
      startIndex = Math.min(startIndex, endIndex);
      range1 = new Integer[]{startIndex, section};
      ncDataLat = NcUtil.getDataWithLimit(variableLat, range1);
    }
    ncGridData.setListLat(ncDataLat.getData1D());
    //添加变量
    NcData variableNcData = NcUtil.getDataOfSlice(variable);
    if (variableNcData.getData2D() != null) {
      /**
       * 如果不需要补偿
       */
      if (!flag) {
        variableNcData = NcUtil.getDataWithLimit(variable, range0, range1, positionOf0, positionOf1);
        ncGridData.setValues(ArrayUtils.flat2D(variableNcData.getData2D()));
      } else {
        float[][] data2D = NcUtil.getDataWithLimit(variable, range0, range1, positionOf0, positionOf1).getData2D();
        float[][] data2D1 = NcUtil.getDataWithLimit(variable, range0AndBound, range1, positionOf0, positionOf1).getData2D();
        float[][] data = ArrayUtils.arrContact(data2D, data2D1);
        ncGridData.setValues(ArrayUtils.flat2D(data));
      }
    } else {
      ncGridData.setValues(variableNcData.getData1D());
    }


    return ncGridData;
  }

  @Override
//  @Cacheable
  public Map<String, NcData> getNcDataOfSlice(NetcdfFile file, String variableName) throws IOException, InvalidRangeException {
    return getNcDataOfSlice(file, variableName, null);
  }

  @Override
//  @Cacheable
  public Map<String, NcData> getNcDataOfSlice(NetcdfFile file, String variableName, Integer positionOf0) throws IOException, InvalidRangeException {
    return getNcDataOfSlice(file, variableName, positionOf0, null);
  }

  @Override
//  @Cacheable
  public Map<String, NcData> getNcDataOfSlice(NetcdfFile file, String variableName, Integer positionOf0, Integer positionOf1) throws IOException, InvalidRangeException {
    Map<String, NcData> map = new HashMap<>();
    Variable variable = getVariable(file, variableName);
    if (variable == null) {
      return map;
    }
    List<Dimension> dimensionList = variable.getDimensions();   //决定variable的所有维度列表
    //添加相关维度变量
    for (Dimension dimension : dimensionList) {
      Variable dimensionVariable = file.findVariable(dimension.getFullName());
      map.put(dimensionVariable.getFullName(), NcUtil.getDataOfSlice(dimensionVariable));
    }
    map.put(variableName, NcUtil.getDataOfSlice(variable, positionOf0, positionOf1));
    return map;
  }

  @Override
//  @Cacheable
  public Map<String, NcData> getNcDataWithLimit(NetcdfFile file, String variableName, Float[] lon, Float[] lat) throws IOException, InvalidRangeException {

    return getNcDataWithLimit(file, variableName, lon, lat, null);
  }

  @Override
//  @Cacheable
  public Map<String, NcData> getNcDataWithLimit(NetcdfFile file, String variableName, Float[] lon, Float[] lat, Integer positionOf0) throws IOException, InvalidRangeException {
    return getNcDataWithLimit(file, variableName, lon, lat, positionOf0, null);
  }

  @Override
//  @Cacheable
  public Map<String, NcData> getNcDataWithLimit(NetcdfFile file, String variableName, Float[] lon, Float[] lat, Integer positionOf0, Integer positionOf1) throws IOException, InvalidRangeException {
    Map<String, NcData> map = new HashMap<>();
    Variable variable = getVariable(file, variableName);
    if (variable == null) {
      return map;
    }
    Integer[] range0 = null;
    Integer[] range1 = null;
    int startIndex;
    int section;
    NcCompensate ncCompensate = new NcCompensate();
    /**
     * 如果传递的经纬度确实有问题
     * 再进行获取补偿值操作
     * flag是是否需要进行拼接
     */
    boolean flag = false;
    if (lon[1] < lon[0] || lat[1] < lat[0]) {
      ncCompensate = getNcCompensate(file, lon, lat, variable, positionOf0, positionOf1);
      flag = true;
    }

    //添加lon
    Variable variableLon = getVariable(file, VariableName.LON);
    /**
     * 之所以这里不再写lon[1]==lon[0]，或者lat[0]==lat[1]是因为如果就拿一条带，
     * 那就是或者这一整层就是NcUtil.getDataOfSlice，不会重新设置
     * 而且也适用于lon数组为空情况
     */
    NcData ncDataLon = NcUtil.getDataOfSlice(variableLon);
    if (StringUtils.isNotEmpty(lon)) {
      if (lon[1] > lon[0]) {
        startIndex = ArrayUtils.getArrayIndex(ncDataLon.getData1D(), lon[0]);
        section = ArrayUtils.getArrayIndex(ncDataLon.getData1D(), lon[1]) - startIndex + 1;
        range0 = new Integer[]{startIndex, section};
        ncDataLon.setData1D(NcUtil.getDataWithLimit(variableLon, range0).getData1D());
      } else if (lon[1] < lon[0]) {
        ncDataLon.setData1D(ncCompensate.getListLon());
      }
    }
    map.put("lon", ncDataLon);
    //添加lat
    Variable variableLat = getVariable(file, VariableName.LAT);
    NcData ncDataLat = NcUtil.getDataOfSlice(variableLat);
    if (StringUtils.isNotEmpty(lat)) {
      if (lat[1] > lat[0]) {
        startIndex = ArrayUtils.getArrayIndex(ncDataLat.getData1D(), lat[0]);
        int endIndex = ArrayUtils.getArrayIndex(ncDataLat.getData1D(), lat[1]);
        section = Math.abs(endIndex - startIndex + 1);

        range1 = new Integer[]{Math.min(startIndex, endIndex), section};
        ncDataLat.setData1D(NcUtil.getDataWithLimit(variableLat, range1).getData1D());
      } else if (lat[1] < lat[0]) {
        ncDataLat.setData1D(ncCompensate.getListLat());
      }
    }
    map.put("lat", ncDataLat);
    //添加level
    Variable level;
    if ((level = getVariable(file, VariableName.LEVEL)) != null) {
      map.put("depth", NcUtil.getDataOfSlice(level));
    }
    //添加变量
    /**
     * 如果没有进行补偿操作
     * 正常获取即可
     */
    if (!flag) {
      map.put(variableName, NcUtil.getDataWithLimit(variable, range0, range1, positionOf0));
    } else {
      NcData ncData = NcUtil.getDataOfSlice(variable, positionOf0, positionOf1);
      if (ncData.getData2D() != null) {
        ncData.setData2D(ncCompensate.getData2D());
      } else if (ncData.getData1D() != null) {
        ncData.setData1D(ncCompensate.getData1D());
      }
      map.put(variableName, ncData);
    }

    return map;
  }

  @Override
//  @Cacheable
  public Map<String, NcData> getNcDataWithLimitByDepth(NetcdfFile file, String variableName, Float[] lon, Float[] lat, Float[] depth) throws IOException, InvalidRangeException {
    Integer[] level = null;
    int startIndex;
    int section;
    Variable variableLevel = getVariable(file, VariableName.LEVEL);
    if (variableLevel != null) {
      NcData ncDataLevel = NcUtil.getDataOfSlice(variableLevel);
      if (depth != null) {
        startIndex = ArrayUtils.getArrayIndex(ncDataLevel.getData1D(), depth[0]);
        section = ArrayUtils.getArrayIndex(ncDataLevel.getData1D(), depth[1]) - startIndex;
        level = new Integer[]{startIndex, section};
      }
    }
    return getNcDataWithLimitByLevel(file, variableName, lon, lat, level);
  }

  @Override
//  @Cacheable
  public Map<String, NcData> getNcDataWithLimitByLevel(NetcdfFile file, String variableName, Float[] lon, Float[] lat, Integer[] level) throws IOException, InvalidRangeException {
    if (level == null) {
      return getNcDataWithLimit(file, variableName, lon, lat);
    }

    Map<String, NcData> map = new HashMap<>();
    Variable variable = getVariable(file, variableName);
    if (variable == null) {
      return map;
    }
    Integer[] range0 = null;
    Integer[] range1 = null;
    int startIndex;
    int section;
    //添加lon
    Variable variableLon = getVariable(file, VariableName.LON);
    if (StringUtils.isNotEmpty(lon)) {
      NcData ncDataLon = NcUtil.getDataOfSlice(variableLon);
      startIndex = ArrayUtils.getArrayIndex(ncDataLon.getData1D(), lon[0]);
      section = ArrayUtils.getArrayIndex(ncDataLon.getData1D(), lon[1]) - startIndex + 1;
      range0 = new Integer[]{startIndex, section};
    }
    map.put("lon", NcUtil.getDataWithLimit(variableLon, range0));
    //添加lat
    Variable variableLat = getVariable(file, VariableName.LAT);
    if (StringUtils.isNotEmpty(lat)) {
      NcData ncDataLat = NcUtil.getDataOfSlice(variableLat);
      startIndex = ArrayUtils.getArrayIndex(ncDataLat.getData1D(), lat[0]);
      int endIndex = ArrayUtils.getArrayIndex(ncDataLat.getData1D(), lat[1]);
      section = Math.abs(endIndex - startIndex + 1);
      range1 = new Integer[]{Math.min(startIndex, endIndex), section};
    }
    map.put("lat", NcUtil.getDataWithLimit(variableLat, range1));

    //添加level
    map.put("depth", NcUtil.getDataOfSlice(getVariable(file, VariableName.LEVEL)));
    //遍历层数，添加变量
    for (Integer i : level) {
      map.put(variableName + i, NcUtil.getDataWithLimit(variable, range0, range1, i));
    }

    return map;
  }

  @Override
//  @Cacheable
  public List<NcIndex> getIndex(NetcdfFile ncFile, float key, String variableName) throws IOException, InvalidRangeException {
    Variable variable = getVariable(ncFile, variableName);
    NcIndex ncIndex = new NcIndex();
    Variable level = getVariable(ncFile, VariableName.LEVEL);
    NcData ncLevel = NcUtil.getDataOfSlice(level);
    List<NcIndex> list = new ArrayList<>();
    if (variable == null) {
      return list;
    }
    float levelIndex;
    float latIndex;
    float lonIndex;
    String[] levelName = VariableName.LEVEL.getVariableName();
    for (int i = 0; i < levelName.length; i++) {
      if (variableName.equals(levelName[i])) {
        NcData ncDataLevel = NcUtil.getDataOfSlice(variable);
        levelIndex = ArrayUtils.getArrayIndex(ncDataLevel.getData1D(), key);
        ncIndex.setLevelIndex(levelIndex + 1);
        list.add(ncIndex);
        return list;
      }
    }
    DecimalFormat df = new DecimalFormat("#.000");
    Variable latVariable = getVariable(ncFile, VariableName.LAT);
    Variable lonVariable = getVariable(ncFile, VariableName.LON);
    NcData latNcData = NcUtil.getDataOfSlice(latVariable);
    NcData lonNcData = NcUtil.getDataOfSlice(lonVariable);
   /* float latStart = (float) Math.round((latNcData.getData1D()[0] * scale)) / scale;
    float lonStart = (float) Math.round((lonNcData.getData1D()[0] * scale)) / scale;*/
    float latStart = Float.parseFloat(df.format(latNcData.getData1D()[0]));
    float lonStart = Float.parseFloat(df.format(lonNcData.getData1D()[0]));
    /*float latStride = new BigDecimal(String.valueOf((float) Math.round((latNcData.getData1D()[1] * scale)) / scale)).subtract(new BigDecimal(String.valueOf(latStart))).floatValue();
    float lonStride = new BigDecimal(String.valueOf((float) Math.round((lonNcData.getData1D()[1] * scale)) / scale)).subtract(new BigDecimal(String.valueOf(lonStart))).floatValue();*/
    float latStride = (new BigDecimal(String.valueOf(df.format(latNcData.getData1D()[1]))).subtract(new BigDecimal(String.valueOf(latStart)))).floatValue();
    float lonStride = new BigDecimal(String.valueOf(df.format(lonNcData.getData1D()[1]))).subtract(new BigDecimal(String.valueOf(lonStart))).floatValue();
    System.out.println("latStride = " + latStride);
    System.out.println("lonStride = " + lonStride);
    NcData ncData;
    if (ncLevel.getData1D() != null) {
      for (int i = 0; i < ncLevel.getData1D().length; i++) {
        ncData = NcUtil.getDataOfSlice(variable, i);
        float[][] data2D = ncData.getData2D();
        for (int j = 0; j < data2D.length; j++) {
          for (int k = 0; k < data2D[0].length; k++) {
            if (!Float.isNaN(data2D[j][k]) && Float.parseFloat(df.format(data2D[j][k])) == key) {
              ncIndex = new NcIndex();
              ncIndex.setLevelIndex(i + 1);
              latIndex = latStart + latStride * j;
              lonIndex = lonStart + lonStride * k;
              ncIndex.setLatIndex(Float.parseFloat(df.format(latIndex)));
              ncIndex.setLonIndex(Float.parseFloat(df.format(lonIndex)));
              list.add(ncIndex);
            }
          }
        }
      }
    } else {
      ncData = NcUtil.getDataOfSlice(variable);
      float[][] data2D = ncData.getData2D();
      for (int j = 0; j < data2D.length; j++) {
        for (int k = 0; k < data2D[0].length; k++) {
          if (!Float.isNaN(data2D[j][k]) && Float.parseFloat(df.format(data2D[j][k])) == key) {
            ncIndex = new NcIndex();
            ncIndex.setLevelIndex(1);
            latIndex = latStart + latStride * j;
            lonIndex = lonStart + lonStride * k;
            ncIndex.setLatIndex(Float.parseFloat(df.format(latIndex)));
            ncIndex.setLonIndex(Float.parseFloat(df.format(lonIndex)));
            list.add(ncIndex);
          }
        }
      }
    }
    return list;
  }

  @Override
//  @Cacheable
  public List<Variable> getVariableExclude(NetcdfFile file) {
    List<Variable> variables = file.getVariables();
    List<Variable> list = new ArrayList<>();
    for (Variable variable : variables) {
      if (variable.getRank() > 1) {
        list.add(variable);
      }
    }
    return list;
  }

  @Override
//  @Cacheable
  public String variableRename(String originName) {
    VariableName[] values = VariableName.values();
    List<List<String>> lists = new ArrayList<>();
    for (VariableName value : values) {
      List<String> list = Arrays.asList(value.getVariableName());
      lists.add(list);
    }
    for (List<String> list : lists) {
      if (list.contains(originName)) {
        return list.get(0);
      }
    }
    return originName;
  }

  @Override
//  @Cacheable
  public Map<String, NcData> getNcDataOfOneDimensional(NetcdfFile file) {
    Map<String, NcData> map = new HashMap<>();
    try {
      List<Variable> variables = file.getVariables();
      for (Variable variable : variables) {
        if (variable.getRank() == 1) {
          String variableName = variable.getShortName();
          map.put(variableName, NcUtil.getDataOfSlice(variable));
        }
      }
    } catch (Exception e) {
      e.printStackTrace();
    }
    return map;
  }

  @Override
//  @Cacheable
  public Map<String, NcData> getNcData(NetcdfFile ncFile, Integer positionOf0, Integer positionOf1) {
    Map<String, NcData> map = new HashMap<>();
    try {
      List<Variable> variables = ncFile.getVariables();

//      List<Future<Map<String, NcData>>> listTasks = new ArrayList<>();
      for (Variable variable : variables) {
        long start = System.currentTimeMillis();
        map.put(variable.getShortName(), NcUtil.getDataOfSlice(variable, positionOf0, positionOf1));
       /* System.out.println("variable.getShortName() = " + variable.getShortName());
        System.setProperty("java.vm.name", "Java HotSpot(TM) ");
        System.out.println(ObjectSizeCalculator.getObjectSize(map) / (1024 * 1024));
        System.out.println("用时 = " + (System.currentTimeMillis() - start));*/
      }
    /*  for (Future<Map<String, NcData>> listTask : listTasks) {
        if (listTask.isDone()) {
          System.out.println("做完 = " + listTask.toString());
        }
      }*/
    } catch (Exception e) {
      e.printStackTrace();
    }
    return map;
  }

  @Async
  public Future<Map<String, NcData>> getNcData(Map<String, NcData> map, Variable variable) throws IOException, InvalidRangeException {
    map.put(variable.getShortName(), NcUtil.getDataOfSlice(variable));
    return new AsyncResult<>(map);
  }

  @Override
//  @Cacheable
  public Map<String, NcData> getNcData(NetcdfFile ncFile, Integer positionOf0) {
    return getNcData(ncFile, positionOf0, null);
  }

  @Override
//  @Cacheable
  public Map<String, NcData> getNcData(NetcdfFile ncFile) {
    return getNcData(ncFile, null, null);
  }


  @Override
//  @Cacheable
  public Map<String, NcData> getNcDataOfLimit(NetcdfFile ncFile, Float[] lon, Float[] lat, Integer depth) {
    return getNcDataOfLimit(ncFile, lon, lat, depth, null);
  }

  @Override
//  @Cacheable
  public Map<String, NcData> getNcDataOfLimit(NetcdfFile ncFile, Float[] lon, Float[] lat, Integer depth, Integer time) {
    Map<String, NcData> map = new HashMap<>();
    Integer[] range0;
    Integer[] range1;
    int startIndex;
    int section;
    int endIndex;
    /**
     * 获取经纬度限制条件
     * 如果有问题，有可能是这个nc文件内部，第一个限制条件不是lon，而是lat
     * 可以考虑把range0和range1位置互换
     */
    try {
      boolean flag = false;
      if (lon[1] < lon[0] || lat[1] < lat[0]) {
        flag = true;
      }
      List<Variable> variables = ncFile.getVariables();
      startIndex = ArrayUtils.getArrayIndex(NcUtil.getDataOfSlice(getVariable(ncFile, VariableName.LON)).getData1D(), lon[0]);
      section = ArrayUtils.getArrayIndex(NcUtil.getDataOfSlice(getVariable(ncFile, VariableName.LON)).getData1D(), lon[1]) - startIndex + 1;
      range0 = new Integer[]{startIndex, section + 1};

      startIndex = ArrayUtils.getArrayIndex(NcUtil.getDataOfSlice(getVariable(ncFile, VariableName.LAT)).getData1D(), lat[0]);
      endIndex = ArrayUtils.getArrayIndex(NcUtil.getDataOfSlice(getVariable(ncFile, VariableName.LAT)).getData1D(), lat[1]);
      section = Math.abs(endIndex - startIndex + 1);
      startIndex = Math.min(startIndex, endIndex);
      range1 = new Integer[]{startIndex, section};
      /**
       * 对于每一个变量进行存储判断
       */
      for (Variable variable : variables) {
        /**
         * 如果变量的所有长度还不够range长度，也就是2
         * 把所有数据拿出来（一般是time）
         */
        if (variable.getSize() < range0.length) {
          map.put(variable.getShortName(), NcUtil.getDataOfSlice(variable));
        } else {
          /**
           * 对于有足够数据量的变量
           */
          NcCompensate ncCompensate = new NcCompensate();
          if (flag) {
            ncCompensate = getNcCompensate(ncFile, lon, lat, variable, depth, time);
          }
          switch (variable.getRank()) {
            //  lon，lat，depth这种已知的一维变量进行限制
            //  如果还有其他一维变量要记得设置
            case 1:
              if (!flag) {
                if (isContainVariableName(variable.getShortName(), VariableName.LON)) {
                  map.put(variable.getShortName(), NcUtil.getDataWithLimit(variable, range0));
                } else if (isContainVariableName(variable.getShortName(), VariableName.LAT)) {
                  map.put(variable.getShortName(), NcUtil.getDataWithLimit(variable, range1));
                } else if (isContainVariableName(variable.getShortName(), VariableName.LEVEL)) {
                  map.put(variable.getShortName(), NcUtil.getDataWithLimit(variable, new Integer[]{depth, 1}));
                } else {
                  map.put(variable.getShortName(), NcUtil.getDataOfSlice(variable));
                }
              } else {
                if (isContainVariableName(variable.getShortName(), VariableName.LON)) {
                  NcData ncData = NcUtil.getDataWithLimit(variable, range0);
                  ncData.setData1D(ncCompensate.getListLon());
                  map.put(variable.getShortName(), ncData);
                } else if (isContainVariableName(variable.getShortName(), VariableName.LAT)) {
                  NcData ncData = NcUtil.getDataWithLimit(variable, range1);
                  ncData.setData1D(ncCompensate.getListLat());
                  map.put(variable.getShortName(), ncData);
                } else if (isContainVariableName(variable.getShortName(), VariableName.LEVEL)) {
                  map.put(variable.getShortName(), NcUtil.getDataWithLimit(variable, new Integer[]{depth, 1}));
                } else {
                  map.put(variable.getShortName(), NcUtil.getDataOfSlice(variable));
                }
              }
              break;
            //  一般没有只有两个维度的数据，最少也是经度，维度，时间
            case 2:
              map.put(variable.getShortName(), NcUtil.getDataWithLimit(variable, range0, range1));
              break;
            //  三维数据，一般是时间做最后一维，所以直接写时间限制是0
            case 3:
              if (!flag) {
                map.put(variable.getShortName(), NcUtil.getDataWithLimit(variable, range0, range1, time));
              } else {
                NcData ncData = NcUtil.getDataWithLimit(variable, range0, range1, time);
                ncData.setData2D(ncCompensate.getData2D());
                map.put(variable.getShortName(), ncData);
              }
              break;
            //  四维数据，一般有时间做最后一维，所以直接不做考虑
            case 4:
              if (!flag) {
                map.put(variable.getShortName(), NcUtil.getDataWithLimit(variable, range0, range1, depth, time));
              } else {
                NcData ncData = NcUtil.getDataWithLimit(variable, range0, range1, depth, time);
                ncData.setData2D(ncCompensate.getData2D());
                map.put(variable.getShortName(), ncData);
              }
              break;
          }
        }
      }
    } catch (Exception e) {
      e.printStackTrace();
    }
    return map;
  }


  @Override
//  @Cacheable
  public Boolean isContainVariableName(String originName, VariableName variableName) {
    boolean flag = false;
    List<String> list = Arrays.asList(variableName.getVariableName());
    if (list.contains(originName)) {
      flag = true;
    }

    return flag;
  }

  @Override
//  @Cacheable
  public Map<Integer, Float> getLevelMap(NetcdfFile ncFile) throws IOException, InvalidRangeException {
    Map<Integer, Float> map = new HashMap<>();
    NcData ncData = NcUtil.getDataOfSlice(getVariable(ncFile, VariableName.LEVEL));
    int i = 0;
    for (float v : ncData.getData1D()) {
      map.put(i, v);
      i++;
    }
    return map;

  }

  @Override
//  @Cacheable
  public String getVariableName(String originName, List<String> variableNameList) {
    for (String s : variableNameList) {
      for (VariableName value : VariableName.values()) {
        if (isContainVariableName(s, value) && isContainVariableName(originName, value)) {
          return s;
        }
      }
    }
    return null;

  }

  @Override
//  @Cacheable
  public NcCompensate getNcCompensate(NetcdfFile ncFile, Float[] lon, Float[] lat, Variable variable, Integer level) throws IOException, InvalidRangeException {
    return getNcCompensate(ncFile, lon, lat, variable, level, null);
  }

  @Override
//  @Cacheable
  public NcCompensate getNcCompensate(NetcdfFile ncFile, Float[] lon, Float[] lat, Variable variable, Integer level, Integer time) throws IOException, InvalidRangeException {
    NcCompensate ncCompensate = new NcCompensate();
    //  获取所有数据
    Map<String, NcData> ncData = getNcDataOfSlice(ncFile, variable.getShortName(), level, time);
    //  获取经度纬度和需要的变量属性值
    NcData lonData = ncData.get(getVariable(ncFile, VariableName.LON).getFullName());
    NcData latData = ncData.get(getVariable(ncFile, VariableName.LAT).getFullName());
    NcData variableData = ncData.get(variable.getFullName());
    Integer[] range0 = null;
    Integer[] range1 = null;
    Integer[] range0AndBound = null;
    int startIndex;
    int section;
    int endIndex;
    Variable variableLon = getVariable(ncFile, VariableName.LON);
    Variable variableLat = getVariable(ncFile, VariableName.LAT);
    //  获取正确的经度两边拼接
    if (lonData != null) {
      startIndex = ArrayUtils.getArrayIndex(lonData.getData1D(), lon[0]);
      endIndex = lonData.getData1D().length - 1;
      section = endIndex - startIndex + 1;
      range0 = new Integer[]{startIndex, section};
      float[] lonData1 = NcUtil.getDataWithLimit(variableLon, range0).getData1D();

      startIndex = 0;
      endIndex = ArrayUtils.getArrayIndex(lonData.getData1D(), lon[1]);
      section = endIndex - startIndex + 1;
      range0AndBound = new Integer[]{startIndex, section};
      float[] lonData2 = NcUtil.getDataWithLimit(variableLon, range0AndBound).getData1D();
      float[] listLon = ArrayUtils.arrContact(lonData1, lonData2);
      ncCompensate.setListLon(listLon);
    }
    if (latData != null) {
      //  获取纬度（北极暂不考虑所有未做改动，以后可以在这里更改）
      startIndex = lat[0] == null ? 0 : ArrayUtils.getArrayIndex(latData.getData1D(), lat[0]);
      endIndex = lat[1] == null ? latData.getData1D().length - 1 : ArrayUtils.getArrayIndex(latData.getData1D(), lat[1]);
      section = Math.abs(endIndex - startIndex) + 1;
      range1 = new Integer[]{Math.min(startIndex, endIndex), section};
      ncCompensate.setListLat(NcUtil.getDataWithLimit(variableLat, range1).getData1D());
    }


    //  设置变量属性数据，进行重新拼接
    if (variableData.getData2D() != null) {
      float[][] data2D0 = NcUtil.getDataWithLimit(variable, range0, range1, level, time).getData2D();
      float[][] data2D1 = NcUtil.getDataWithLimit(variable, range0AndBound, range1, level, time).getData2D();
      float[][] data2D = ArrayUtils.arrContact(data2D0, data2D1);
      ncCompensate.setData2D(data2D);
    }
    /**
     * 对于二维数据肯定是先拼接好数据，至于一维数据
     * 虽然这里设置了补偿数据为一维数据，但是如果之前这个变量的数据是二位数据
     * 经过扁平化之后得到一个一维数据，不能使用这个ncCompensate的一维数据
     * 应该使用新二维数据再扁平
     * 一般走到这里都是lat或者lon或者time或者depth
     */
    else if (variableData.getData1D() != null) {
      ncCompensate.setData1D(variableData.getData1D());
    }
    return ncCompensate;
  }


  @Override
//  @Cacheable
  public Map<String, NcData> getNcDataOfSliceFromVariable(NetcdfFile ncFile, String variableName, Integer positionOf0, Integer positionOf1) throws IOException, InvalidRangeException {
    Variable variable = getVariable(ncFile, variableName);
    Map<String, NcData> map = new HashMap<>();
    if (variable == null) {
      return map;
    }
    List<Dimension> dimensionList = variable.getDimensions();   //决定variable的所有维度列表
    //添加相关维度变量
    for (Dimension dimension : dimensionList) {
      Variable dimensionVariable = ncFile.findVariable(dimension.getFullName());
      map.put(dimensionVariable.getShortName(), NcUtil.getDataOfSlice(dimensionVariable));
    }
    map.put(variable.getShortName(), NcUtil.getDataOfSlice(variable, positionOf0, positionOf1));
    return map;
  }

  @Override
//  @Cacheable
  public Map<String, NcData> getNcDataOfSliceFromVariable(NetcdfFile ncFile, String variableName, Integer positionOf0) throws IOException, InvalidRangeException {
    return getNcDataOfSliceFromVariable(ncFile, variableName, positionOf0, null);
  }

  @Override
//  @Cacheable
  public Map<String, NcData> getNcDataOfSliceFromVariable(NetcdfFile ncFile, String variableName) throws IOException, InvalidRangeException {
    return getNcDataOfSliceFromVariable(ncFile, variableName, null, null);
  }

  @Override
//  @Cacheable
  public Map<String, NcData> getNcDataOfLimitFromVariable(NetcdfFile ncFile, String variableName, Float[] lon, Float[] lat, Integer positionOf0, Integer positionOf1) throws IOException, InvalidRangeException {
    Variable variable = getVariable(ncFile, variableName);
    if (variable == null) {
      return null;
    }
    Map<String, NcData> ncDataMap = new HashMap<>();

    //设置经纬度
    Integer[] range0 = null;
    Integer[] range1 = null;
    Integer[] range0AndBound = null;
    int startIndex;
    int section;
    int endIndex;
    Variable variableLon = getVariable(ncFile, VariableName.LON);
    //  默认不需要进行拼接
    boolean flag = false;
    NcData ncDataLon = NcUtil.getDataOfSlice(variableLon);
    if (StringUtils.isNotEmpty(lon)) {
      if (lon[1] == null || lon[0] == null || lon[1] >= lon[0]) {
        startIndex = lon[0] == null ? 0 : ArrayUtils.getArrayIndex(ncDataLon.getData1D(), lon[0]);
        endIndex = lon[1] == null ? ncDataLon.getData1D().length - 1 : ArrayUtils.getArrayIndex(ncDataLon.getData1D(), lon[1]);
        section = Math.abs(endIndex - startIndex) + 1;
        range0 = new Integer[]{startIndex, section};
        ncDataLon = NcUtil.getDataWithLimit(variableLon, range0);
      }
      /**
       * 适用于开始经度大于结束经度
       * 一般是考虑地球球体原因导致
       */
      else if (lon[1] < lon[0]) {
        //  第一部分（设定的开始经度到nc文件经度结尾）
        startIndex = ArrayUtils.getArrayIndex(ncDataLon.getData1D(), lon[0]);
        endIndex = ncDataLon.getData1D().length - 1;
        section = endIndex - startIndex + 1;
        range0 = new Integer[]{startIndex, section};
        float[] lonData1 = NcUtil.getDataWithLimit(variableLon, range0).getData1D();
        //  第二部分（设定的开始经度到nc文件经度结尾）
        startIndex = 0;
        endIndex = ArrayUtils.getArrayIndex(ncDataLon.getData1D(), lon[1]);
        section = endIndex - startIndex + 1;
        range0AndBound = new Integer[]{startIndex, section};
        float[] lonData2 = NcUtil.getDataWithLimit(variableLon, range0AndBound).getData1D();
        //  进行数组拼接
        float[] listLon = ArrayUtils.arrContact(lonData1, lonData2);
        ncDataLon.setData1D(listLon);
        flag = true;
      }
    }
    ncDataMap.put(variableLon.getShortName(), ncDataLon);

    Variable variableLat = getVariable(ncFile, VariableName.LAT);
    NcData ncDataLat = NcUtil.getDataOfSlice(variableLat);
    if (StringUtils.isNotEmpty(lat)) {
      startIndex = lat[0] == null ? 0 : ArrayUtils.getArrayIndex(ncDataLat.getData1D(), lat[0]);
      endIndex = lat[1] == null ? ncDataLat.getData1D().length - 1 : ArrayUtils.getArrayIndex(ncDataLat.getData1D(), lat[1]);
      section = Math.abs(endIndex - startIndex) + 1;
      range1 = new Integer[]{Math.min(startIndex, endIndex), section};
      ncDataLat = NcUtil.getDataWithLimit(variableLat, range1);
    }

    ncDataMap.put(variableLat.getShortName(), ncDataLat);

    //添加变量
    NcData variableNcData = NcUtil.getDataWithLimit(variable, range0, range1, positionOf0);
    /**
     * 如果是二维数据
     */
    if (variableNcData.getData2D() != null) {
      /**
       * 如果不需要补偿
       */
      if (!flag) {
        variableNcData = NcUtil.getDataWithLimit(variable, range0, range1, positionOf0, positionOf1);
      } else {
        float[][] data2D = NcUtil.getDataWithLimit(variable, range0, range1, positionOf0, positionOf1).getData2D();
        float[][] data2D1 = NcUtil.getDataWithLimit(variable, range0AndBound, range1, positionOf0, positionOf1).getData2D();
        float[][] data = ArrayUtils.arrContact(data2D, data2D1);
        variableNcData.setData2D(data);
      }
    } else {
      variableNcData.setData1D(NcUtil.getDataWithLimit(variable, range0, range1, positionOf0).getData1D());
    }
    ncDataMap.put(variable.getShortName(), variableNcData);
    List<Dimension> dimensions = variable.getDimensions();
    for (Dimension dimension : dimensions) {
      if (!ncDataMap.containsKey(dimension.getShortName())) {
        ncDataMap.put(dimension.getShortName(), NcUtil.getDataOfSlice(ncFile.findVariable(dimension.getShortName())));
      }

    }

    return ncDataMap;
  }

  @Override
//  @Cacheable
  public Map<String, NcData> getNcDataOfLimitFromVariable(NetcdfFile ncFile, String variableName, Float[] lon, Float[] lat, Integer positionOf0) throws IOException, InvalidRangeException {
    return getNcDataOfLimitFromVariable(ncFile, variableName, lon, lat, positionOf0, null);
  }

  @Override
//  @Cacheable
  public Map<String, NcData> getNcDataOfLimitFromVariable(NetcdfFile ncFile, String variableName, Float[] lon, Float[] lat) throws IOException, InvalidRangeException {
    return getNcDataOfLimitFromVariable(ncFile, variableName, lon, lat, null, null);
  }

  @Override
//  @Cacheable
  public FlowData getParticleFlowData(float[][] value, float[] lonArr, float[] latArr, int dilution, int exaggerate, VariableName variableName, boolean reverseFlag, boolean startByZero) {
    FlowData flowData = new FlowData();
    Header header = new Header();
    /**
     * 设置头信息
     * 固定写法（data.json这么写的）
     */
    header.setParameterCategory(2);
    header.setRefTime("2018-06-25T00:00:00.000Z");
    header.setParameterUnit("m.s-1");

    /**
     * 经纬度进行稀释
     */
    latArr = ArrayUtils.arr1DDilution(latArr, dilution);
    lonArr = ArrayUtils.arr1DDilution(lonArr, dilution);

    /**
     * 二维数组进行稀释
     * 行和列分别稀释
     */
    float[][] data2D = ArrayUtils.arr2DDilution(value, dilution, dilution);
    /**
     * 二维数组拼接从最后一行开始拼接
     */
    float[] data;
    if (reverseFlag) {
      data = ArrayUtils.flat2DFormFromLast(data2D);
    } else {
      data = ArrayUtils.flat2D(data2D);
    }
    /**
     * 因为数组不能设置null
     * 所以使用list设置null值
     * 如果是NaN，设置为null
     */
    List<Float> list = new ArrayList<>();
    for (float datum : data) {
      if (Float.isNaN(datum)) {
        list.add(null);
      } else {
        /**
         * 二维数组乘固定倍数，可以使数据显示更明显（可以进行更改）
         */
        list.add((float) Arith.round(BigDecimal.valueOf(datum).multiply(BigDecimal.valueOf(exaggerate)).doubleValue(), 2));
      }
    }
    //设置数据
    flowData.setData(list);
    header.setNx(lonArr.length);
    header.setNy(latArr.length);
    /*
     * 如果是从0开始到360度认为不需要加180度
     * 否则lon应该从0~360，lat应该从-90~90
     * 所以数据可能要加减开始经度纬度，如果超出界限要减360和180*/
    if (startByZero) {
      header.setLo1(lonArr[0]);
      header.setLo2(lonArr[lonArr.length - 1]);
    } else {
      header.setLo1(lonArr[0] + 180 > 360 ? (lonArr[0] - 180) : lonArr[0] + 180);
      header.setLo2(lonArr[lonArr.length - 1] + 180 > 360 ? (lonArr[lonArr.length - 1] - 180) : lonArr[lonArr.length - 1] + 180);
    }
    header.setLa1(Math.max(latArr[latArr.length - 1], latArr[0]));
    header.setLa2(Math.min(latArr[latArr.length - 1], latArr[0]));
    //  设置经纬度间隔
    float dx = (float) Arith.round(BigDecimal.valueOf(lonArr[1]).subtract(BigDecimal.valueOf(lonArr[0])).floatValue(), 2);
    float dy = (float) Arith.round(BigDecimal.valueOf(latArr[1]).subtract(BigDecimal.valueOf(latArr[0])).floatValue(), 2);
    header.setDx(dx);
    header.setDy(dy);
    if (variableName.equals(VariableName.UFLOW)) {
      header.setParameterNumber(2);
    } else if (variableName.equals(VariableName.VFLOW)) {
      header.setParameterNumber(3);
    } else {
      return new FlowData();
    }
    flowData.setHeader(header);
    return flowData;
  }

  @Override
//  @Cacheable
  public FlowData getParticleFlowDataWithWind(float[][] value, float[] lonArr, float[] latArr, int dilution, int exaggerate, VariableName variableName) {
    FlowData flowData = new FlowData();
    Header header = new Header();
    /**
     * 设置头信息
     * 固定写法（data.json这么写的）
     */
    header.setParameterCategory(2);
    header.setRefTime("2018-06-25T00:00:00.000Z");
    header.setParameterUnit("m.s-1");

    /**
     * 经纬度进行稀释
     */
    latArr = ArrayUtils.arr1DDilution(latArr, dilution);
    lonArr = ArrayUtils.arr1DDilution(lonArr, dilution);

    /**
     * 二维数组进行稀释
     * 行和列分别稀释
     */
    float[][] data2D = ArrayUtils.arr2DDilution(value, dilution, dilution);
    /**
     * 二维数组拼接从最后一行开始拼接
     */
    float[] data = ArrayUtils.flat2D(data2D);
    /**
     * 因为数组不能设置null
     * 所以使用list设置null值
     * 如果是NaN，设置为null
     */
    List<Float> list = new ArrayList<>();
    for (float datum : data) {
      if (Float.isNaN(datum)) {
        list.add(null);
      } else {
        /**
         * 二维数组乘固定倍数，可以使数据显示更明显（可以进行更改）
         */
        list.add((float) Arith.round(BigDecimal.valueOf(datum).multiply(BigDecimal.valueOf(exaggerate)).doubleValue(), 2));
      }
    }
    /**
     * 设置数据
     */
    flowData.setData(list);
    header.setNx(lonArr.length);
    header.setNy(latArr.length);
    /**
     * lon应该从0~360，lat应该从-90~90
     * 所以数据可能要加减开始经度纬度，如果超出界限要减360和180
     * 开始纬度应该
     */
    header.setLo1(lonArr[0]);
    header.setLo2(lonArr[lonArr.length - 1]);
    header.setLa1(Math.max(latArr[0], latArr[latArr.length - 1]));
    header.setLa2(Math.min(latArr[0], latArr[latArr.length - 1]));
    //  设置经纬度间隔
    float dx = (float) Arith.round(BigDecimal.valueOf(lonArr[1]).subtract(BigDecimal.valueOf(lonArr[0])).floatValue(), 2);
    float dy = Math.abs((float) Arith.round(BigDecimal.valueOf(latArr[1]).subtract(BigDecimal.valueOf(latArr[0])).floatValue(), 2));
    header.setDx(dx);
    header.setDy(dy);
    if (variableName.equals(VariableName.WINDU)) {
      header.setParameterNumber(2);
    } else if (variableName.equals(VariableName.WINDV)) {
      header.setParameterNumber(3);
    } else {
      return new FlowData();
    }
    flowData.setHeader(header);
    return flowData;
  }

  @Override
//  @Cacheable
  public NcRecord getData3DWithLimit(NetcdfFile ncFile, String variableName, Float[] lon, Float[] lat) throws IOException, InvalidRangeException {
    return getData3DWithLimit(ncFile, variableName, lon, lat, null);
  }

  @Override
//  @Cacheable
  public NcRecord getData3DWithLimit(NetcdfFile ncFile, String variableName, Float[] lon, Float[] lat, Integer positionOf0) throws IOException, InvalidRangeException {
    Variable variable = getVariable(ncFile, variableName);
    NcRecord ncRecord = new NcRecord();
    /*因为获取某一层的数据早就有比较好的方法了
     * 这个方法只是为了获取三维数据，
     * nc文件一般都会有时间，经度，纬度
     * 所以如果不是四维数据就不需要使用这个方法
     * 重复：这个方法只是为了获取所有层限制经纬度的数据，对于一层数据有更好的方法*/
    if (variable == null) {
      return new NcRecord();
    }
    //设置经纬度
    Integer[] range0;
    Integer[] range1;
    Integer[] range0AndBound;
    int startIndex;
    int section;
    int endIndex;
    Variable variableLon = getVariable(ncFile, VariableName.LON);
    Variable variableLat = getVariable(ncFile, VariableName.LAT);
    NcData ncDataLon = NcUtil.getDataOfSlice(variableLon);
    NcData ncDataLat = NcUtil.getDataOfSlice(variableLat);

    //  如果不需要进行拼接
    if (lon[1] >= lon[0]) {
      startIndex = ArrayUtils.getArrayIndex(ncDataLon.getData1D(), lon[0]);
      endIndex = ArrayUtils.getArrayIndex(ncDataLon.getData1D(), lon[1]);
      section = Math.abs(endIndex - startIndex) + 1;
      range0 = new Integer[]{startIndex, section};
      ncRecord.setData1D(NcUtil.getDataWithLimit(variableLon, range0).getData1D());

      startIndex = ArrayUtils.getArrayIndex(ncDataLat.getData1D(), lat[0]);
      endIndex = ArrayUtils.getArrayIndex(ncDataLat.getData1D(), lat[1]);
      section = Math.abs(endIndex - startIndex) + 1;
      range1 = new Integer[]{Math.min(startIndex, endIndex), section};
      if (NcUtil.getAllDataOfVariableWithLimit(variable, range0, range1, positionOf0).getData3D() != null) {
        ncRecord.setData3D(NcUtil.getAllDataOfVariableWithLimit(variable, range0, range1, positionOf0).getData3D());
      }
      if (NcUtil.getAllDataOfVariableWithLimit(variable, range0, range1, positionOf0).getData2D() != null) {
        ncRecord.setData2D(NcUtil.getAllDataOfVariableWithLimit(variable, range0, range1, positionOf0).getData2D());
      }
      return ncRecord;
    }
    /**
     * 如果需要进行拼接
     * 则三维数组进行拼接
     */
    else {
      startIndex = ArrayUtils.getArrayIndex(ncDataLon.getData1D(), lon[0]);
      endIndex = ncDataLon.getData1D().length - 1;
      section = endIndex - startIndex + 1;
      range0 = new Integer[]{startIndex, section};
      float[] lonData1 = NcUtil.getDataWithLimit(variableLon, range0).getData1D();

      startIndex = 0;
      endIndex = ArrayUtils.getArrayIndex(ncDataLon.getData1D(), lon[1]);
      section = endIndex - startIndex + 1;
      range0AndBound = new Integer[]{startIndex, section};

      float[] lonData2 = NcUtil.getDataWithLimit(variableLon, range0AndBound).getData1D();
      ncRecord.setData1D(ArrayUtils.arrContact(lonData1, lonData2));

      startIndex = ArrayUtils.getArrayIndex(ncDataLat.getData1D(), lat[0]);
      endIndex = ArrayUtils.getArrayIndex(ncDataLat.getData1D(), lat[1]);


      section = Math.abs(endIndex - startIndex) + 1;
      range1 = new Integer[]{Math.min(startIndex, endIndex), section};

      NcRecord record0 = NcUtil.getAllDataOfVariableWithLimit(variable, range0, range1, positionOf0);
      NcRecord record1 = NcUtil.getAllDataOfVariableWithLimit(variable, range0AndBound, range1, positionOf0);
      if (record0.getData3D() != null && record1.getData3D() != null) {
        float[][][] data3D = new float[record0.getData3D().length][][];
        for (int i = 0; i < record0.getData3D().length; i++) {
          float[][] floats = ArrayUtils.arrContact(record0.getData3D()[i], record1.getData3D()[i]);
          data3D[i] = floats;
        }
        record0.setData3D(data3D);
      }
      if (record0.getData2D() != null && record1.getData2D() != null) {
        record0.setData2D(ArrayUtils.arrContact(record0.getData2D(), record1.getData2D()));
      }
      return record0;
    }

  }

  @Override
  public String getNcTime(NetcdfFile ncFile) throws IOException, InvalidRangeException, ParseException {
    Variable timeVariable = getVariable(ncFile, VariableName.TIME);
    float[] time = NcUtil.getDataOfSlice(timeVariable).getData1D();
    String unitsString = timeVariable.getUnitsString();
    String[] since_s = unitsString.split("since ");

    Date date = DateUtils.parseDate(since_s[1]);
    if (StringUtils.isMatch("*hours*", unitsString)) {
      return DateUtils.parseDateToStr("yyyy-MM-dd", DateUtils.getTimeFormDifference(date, (int) time[0]));
    }
    if (StringUtils.isMatch("*days*", unitsString)) {
      return DateUtils.parseDateToStr("yyyy-MM-dd", DateUtils.getTimeFormDifference(date, (int) time[0] * 24));
    }
    if (StringUtils.isMatch("*minutes*", unitsString)) {
      return DateUtils.parseDateToStr("yyyy-MM-dd", DateUtils.getTimeFormDifference(date, (int) Arith.div(time[0], 60)));
    }
    return "";
  }

  @Override
  public List<String> getNcTimeForList(NetcdfFile ncFile) throws IOException, InvalidRangeException, ParseException {
    Variable variable = getVariable(ncFile, VariableName.TIME);
    long size = variable.getSize();
    return getNcTimeForList(ncFile, 0, (int) size);
  }

  @Override
  public List<String> getNcTimeForList(NetcdfFile ncFile, int startIndex, int endIndex) throws IOException, InvalidRangeException, ParseException {
    Variable timeVariable = getVariable(ncFile, VariableName.TIME);
    float[] time = NcUtil.getDataOfSlice(timeVariable).getData1D();
    for (int i = startIndex; i < endIndex; i++) {
      if (time[i] < 0) {
        time[i] = Math.round(49710 + time[i]);
      }
    }
    String unitsString = timeVariable.getUnitsString();
    String[] since_s = unitsString.split(" since ");
    Date date = DateUtils.parseDate(since_s[1], "yyyy-MM-dd HH:mm:ss");
    List<String> list = new ArrayList<>();
    if (StringUtils.isMatch("*hours*", unitsString)) {
      for (int i = startIndex; i < time.length; i++) {
        list.add(DateUtils.parseDateToStr("yyyy-MM-dd", DateUtils.getTimeFormDifference(date, (int) time[i])));
      }
    }
    if (StringUtils.isMatch("*days*", unitsString)) {
      for (int i = startIndex; i < time.length; i++) {
        list.add(DateUtils.parseDateToStr("yyyy-MM-dd", DateUtils.getTimeFormDifference(date, (int) time[i] * 24)));
      }
    }
    if (StringUtils.isMatch("*minutes*", unitsString)) {
      for (int i = startIndex; i < time.length; i++) {
        list.add(DateUtils.parseDateToStr("yyyy-MM-dd", DateUtils.getTimeFormDifference(date, (int) Arith.div(time[i], 60))));
      }
    }
    return list;
  }

  @Override
  public Integer getNcTimeIndex(NetcdfFile ncFile, Date date) throws IOException, InvalidRangeException {
    Variable timeVariable = getVariable(ncFile, VariableName.TIME);
    NcData timeOfNcData = NcUtil.getDataOfSlice(timeVariable);
    for (int i = 0; i < timeOfNcData.getData1D().length; i++) {
      if (timeOfNcData.getData1D()[i] < 0) {
        timeOfNcData.getData1D()[i] = Math.round(49710 + timeOfNcData.getData1D()[i]);
      }
    }
    if (timeOfNcData.getData1D().length == 1) {
      return 0;
    }
    String unitsString = timeVariable.getUnitsString();
    String[] since_s = unitsString.split(" since ");
    if (StringUtils.isMatch("*hours*", unitsString)) {
      Date startDate = DateUtils.parseDate(since_s[1]);
      long hours = DateUtils.differentSecondByMillisecond(startDate, date) / (60 * 60);
      return ArrayUtils.getArrayIndex(timeOfNcData.getData1D(), hours);
    } else if (StringUtils.isMatch("*days*", unitsString)) {
      Date startDate = DateUtils.parseDate(since_s[1]);
      float days = (float) (DateUtils.differentSecondByMillisecond(startDate, date) / (60.0 * 60 * 24));
      return ArrayUtils.getArrayIndex(timeOfNcData.getData1D(), days);
    } else if (StringUtils.isMatch("*minutes*", unitsString)) {
      Date startDate = DateUtils.parseDate(since_s[1]);
      long minutes = DateUtils.differentSecondByMillisecond(startDate, date) / (60);
      return ArrayUtils.getArrayIndex(timeOfNcData.getData1D(), minutes);
    }
    return null;
  }

  //@Cacheable
  @Override
  public NetcdfFile getNcFile(String filePath) throws IOException {
    NetcdfFile ncFile = NetcdfFile.open(filePath);
    return ncFile;
  }

  @Override
  public float[] getLevel(NetcdfFile ncFile) throws IOException {
    return NcUtil.getLevel(getVariable(ncFile, VariableName.LEVEL));
  }

  @Override
  public NcUploadInfo getNcUploadInfo(NetcdfFile ncFile) {

    List<Variable> variableExcludeList = getVariableExclude(ncFile);
    Map<String, NcData> ncDataOfOneDimensional = getNcDataOfOneDimensional(ncFile);
    float[] lon = ncDataOfOneDimensional.get(getVariable(ncFile, VariableName.LON).getShortName()).getData1D();
    float[] lat = ncDataOfOneDimensional.get(getVariable(ncFile, VariableName.LAT).getShortName()).getData1D();
    String variableName;
    float[] level = null;
    float lonInterval = (float) Arith.round(lon[1] - lon[0], 4);
    float latInterval = (float) Arith.round(lat[1] - lat[0], 4);
    float startLon = lon[0];
    float endLon = lon[lon.length - 1];
    float startLat = lat[0];
    float endLat = lat[lat.length - 1];

    if (getVariable(ncFile, VariableName.LEVEL) != null && ncDataOfOneDimensional.get(getVariable(ncFile, VariableName.LEVEL).getShortName()).getData1D() != null) {
      level = ncDataOfOneDimensional.get(getVariable(ncFile, VariableName.LEVEL).getShortName()).getData1D();
    }
    Date fileTime = DateUtils.dateTime("yyyyMMdd", StringUtils.getContinuityNum(FileUtils.getName(ncFile.getLocation()), 8));
    // 对变量名进行重新命名
    List<String> variableList = new ArrayList<>();
    for (Variable variable : variableExcludeList) {
      variableList.add(variableRename(variable.getShortName()));
    }
    if (variableList.size() > 1) {
      variableName = NcConstants.MULTY;
    } else {
      variableName = variableList.get(0);
    }
    List<String> currentList = Arrays.asList(VariableName.UFLOW.getVariableName()[0], VariableName.VFLOW.getVariableName()[0]);
    List<String> windList = Arrays.asList(VariableName.WINDU.getVariableName()[0], VariableName.WINDV.getVariableName()[0]);
    List<String> waveList = Arrays.asList(VariableName.WAVEU.getVariableName()[0], VariableName.WAVEV.getVariableName()[0]);

    if (variableList.containsAll(currentList)) {
      variableList.add(NcConstants.CURRENTUV);
    }
    if (variableList.containsAll(windList)) {
      variableList.add(NcConstants.WINDUV);
    }
    if (variableList.containsAll(waveList)) {
      variableList.add(NcConstants.WAVEUV);
    }
    return new NcUploadInfo(fileTime, variableName, variableList.toString(), startLon, endLon, startLat, endLat, lonInterval, latInterval, level.length, ncFile.getLocation());

  }

  @Override
  public Map<String, NcData> getLonAndLat(NetcdfFile ncFile) throws IOException, InvalidRangeException {
    Map<String, NcData> map = new HashMap<>();
    map.put("lon", NcUtil.getDataOfSlice(getVariable(ncFile, VariableName.LON)));
    map.put("lat", NcUtil.getDataOfSlice(getVariable(ncFile, VariableName.LAT)));
    return map;
  }

  public NcRecord getData3DOfVariables(NetcdfFile ncFile, String variableName) throws IOException, InvalidRangeException {
    Variable variable = getVariable(ncFile, variableName);
    /*因为获取某一层的数据早就有比较好的方法了
     * 这个方法只是为了获取三维数据，
     * nc文件一般都会有时间，经度，纬度
     * 所以如果不是四维数据就不需要使用这个方法
     * 重复：这个方法只是为了获取所有层限制经纬度的数据，对于一层数据有更好的方法*/
    if (variable == null) {
      return new NcRecord();
    }
    return NcUtil.getAllDataOfVariable(variable);
  }

  @Override
  public Float[][] getDataLineOfVariable(NetcdfFile ncFile, Float startLat, Float startLon, Float endLat, Float endLon,
                                         String variableName, Integer position, Integer pointNum) throws IOException, InvalidRangeException {

    Map<String, NcData> dataMap = getNcDataOfLimitFromVariable(ncFile, variableName, new Float[]{min(startLon, endLon) - 1f, max(startLon, endLon) + 1f}, new Float[]{min(startLat, endLat) - 1f, max(startLat, endLat) + 1f}, position);
//    Map<String, NcData> dataMap = getNcDataWithLimit(ncFile, variableName, new Float[]{min(startLon,endLon), max(startLon,endLon) + 1f}, new Float[]{min(startLat,endLat), max(startLat,endLat) + 1f});
//    Map<String, NcData> dataMap1 = getNcDataOfLimitFromVariable(ncFile, variableName, new Float[]{min(startLon,endLon), max(startLon,endLon) + 1f}, new Float[]{min(startLat,endLat), max(startLat,endLat) + 1f});
//    Variable variable = getVariable(ncFile, variableName);
//    String shortName = getVariable(ncFile, "lat").getShortName();
    float[][] data2D = dataMap.get(getVariable(ncFile,variableName).getShortName()).getData2D();
    float[] latArr = dataMap.get(getVariable(ncFile, "lat").getShortName()).getData1D();
    float[] lonArr = dataMap.get(getVariable(ncFile, "lon").getShortName()).getData1D();
    int latNum = latArr.length;
    int lonNum = lonArr.length;
    Float[][] result = new Float[pointNum][3];
    float dxLon = (endLon - startLon) / (pointNum - 1);
    float dxLat = (endLat - startLat) / (pointNum - 1);
    for (int i = 0; i < pointNum; i++) {
      float x = startLat + i * dxLat;
      float y = startLon + i * dxLon;
      int l = 0, r = latNum - 1;
      while (l < r - 1) {
        int mid = (l + r) / 2;
        if (latArr[mid] > x) {
          r = mid;
        } else {
          l = mid;
        }
      }
      int lat1 = l, lat2 = r;
      l = 0;
      r = lonNum - 1;
      while (l < r - 1) {
        int mid = (l + r) / 2;
        if (lonArr[mid] > y) {
          r = mid;
        } else {
          l = mid;
        }
      }
      int lon1 = l, lon2 = r;
      if (data2D[lat1][lon1] == NaN || data2D[lat1][lon2] == NaN || data2D[lat2][lon1] == NaN || data2D[lat2][lon2] == NaN) {
        result[i][0] = x;
        result[i][1] = y;
        result[i][2] = NaN;
      } else if (x < latArr[lat1] || x > latArr[lat2] || y < lonArr[lon1] || y > lonArr[lon2]) {
        result[i][0] = x;
        result[i][1] = y;
        result[i][2] = NaN;
      } else {
        result[i][0] = x;
        result[i][1] = y;
        result[i][2] = MathUtils.bilinearInterpolation(latArr[lat1], latArr[lat2], lonArr[lon1], lonArr[lon2], x, y, data2D[lat1][lon1], data2D[lat1][lon2], data2D[lat2][lon1], data2D[lat2][lon2]);
//        System.out.println("result = " + result[i][2]);
      }
    }
    return result;
  }

  @Override
  public Float[][] getDataPointOfVariable(NetcdfFile ncFile, Float startLat, Float startLon, String variableName, Integer position) throws IOException, InvalidRangeException {
    Map<String, NcData> dataMap = getNcDataWithLimit(ncFile, variableName, new Float[]{startLon - 1f, startLon + 1f}, new Float[]{startLat - 1f, startLat + 1f}, position);
    float[][] data2D = dataMap.get(getVariable(ncFile, variableName).getShortName()).getData2D();
    float[] latArr = dataMap.get(getVariable(ncFile, "lat").getShortName()).getData1D();
    float[] lonArr = dataMap.get(getVariable(ncFile, "lon").getShortName()).getData1D();
    int latNum = latArr.length;
    int lonNum = lonArr.length;
    Float[][] result = new Float[1][3];
    int l = 0, r = latNum - 1;
    while (l < r - 1) {
      int mid = (l + r) / 2;
      if (latArr[mid] > startLat) {
        r = mid;
      } else {
        l = mid;
      }
    }
    int lat1 = l, lat2 = r;
    l = 0;
    r = lonNum - 1;
    while (l < r - 1) {
      int mid = (l + r) / 2;
      if (lonArr[mid] > startLon) {
        r = mid;
      } else {
        l = mid;
      }
    }
    int lon1 = l, lon2 = r;
    if (data2D[lat1][lon1] == NaN || data2D[lat1][lon2] == NaN || data2D[lat2][lon1] == NaN || data2D[lat2][lon2] == NaN) {
      result[0][0] = startLat;
      result[0][1] = startLon;
      result[0][2] = NaN;
    } else if (startLat < latArr[lat1] || startLat > latArr[lat2] || startLon < lonArr[lon1] || startLon > lonArr[lon2]) {
      result[0][0] = startLat;
      result[0][1] = startLon;
      result[0][2] = NaN;
    } else {
      result[0][0] = startLat;
      result[0][1] = startLon;
      result[0][2] = MathUtils.bilinearInterpolation(latArr[lat1], latArr[lat2], lonArr[lon1], lonArr[lon2], startLat, startLon, data2D[lat1][lon1], data2D[lat1][lon2], data2D[lat2][lon1], data2D[lat2][lon2]);
    }
    return result;
  }

}